/**
 * Copyright (C) 2009 Matteo Merli <mm@merlimat.org>
 * 
 * $Id: PxlTuple.java 24 2009-01-19 19:49:05Z matteo.merli $
 * $URL: http://pxl-lang.googlecode.com/svn/trunk/src/main/java/pxl/types/PxlTuple.java $
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *         http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package pxl.types;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import pxl.errors.IndexError;
import pxl.errors.TypeError;
import pxl.errors.UnsupportedOperand;
import pxl.types.annotations.Exported;

import com.google.common.base.Join;
import com.google.common.collect.Lists;

public final class PxlTuple extends PxlObject
{

    private static final List<PxlObject> emptyTuple = Lists.newArrayList();

    private final List<PxlObject> tuple;

    public PxlTuple()
    {
        tuple = emptyTuple;
    }

    public PxlTuple( PxlObject... items )
    {
        tuple = Lists.newArrayList( items );
    }

    public PxlTuple( Collection<? extends PxlObject> collection )
    {
        tuple = Lists.newArrayList( collection );
    }

    private PxlTuple( int size )
    {
        tuple = Lists.newArrayListWithExpectedSize( size );
    }

    @Override
    public String getType()
    {
        return "Tuple";
    }

    @Override
    public String toString()
    {
        StringBuilder sb = new StringBuilder();
        sb.append( '(' );
        Join.join( sb, ", ", tuple );
        sb.append( ')' );
        return sb.toString();
    }

    public List<PxlObject> getValue()
    {
        return tuple;
    }

    // // Operators

    @Exported
    @Override
    public PxlString __repr__()
    {
        return new PxlString( toString() );
    }

    @Exported
    @Override
    public PxlString __str__()
    {
        return __repr__();
    }

    @Exported
    @Override
    public PxlObject __add__( PxlObject other )
    {
        if ( other instanceof PxlTuple )
        {
            // Concatente the 2 tuples
            PxlTuple newTuple = new PxlTuple( tuple.size()
                    + ((PxlTuple) other).tuple.size() );

            newTuple.tuple.addAll( tuple );
            newTuple.tuple.addAll( ((PxlTuple) other).tuple );

            return newTuple;
        }

        throw new UnsupportedOperand( "+", this, other );
    }

    @Exported
    @Override
    public PxlObject __mul__( PxlObject other )
    {
        if ( other instanceof PxlInteger )
        {

            int n = (int) ((PxlInteger) other).getValue();
            // Concatente the n times
            PxlTuple newTuple = new PxlTuple( tuple.size() * n );
            for ( int i = 0; i < n; i++ )
                newTuple.tuple.addAll( tuple );

            return newTuple;
        }

        throw new UnsupportedOperand( "*", this, other );
    }

    @Override
    public PxlBoolean __eq__( PxlObject other )
    {
        if ( other instanceof PxlTuple )
        {
            PxlTuple otherTuple = (PxlTuple) other;
            if ( tuple.equals( otherTuple.tuple ) )
                return PxlBoolean.True;
        }

        return PxlBoolean.False;
    }

    @Exported
    @Override
    public PxlBoolean __bool__()
    {
        return PxlBoolean.valueOf( !tuple.isEmpty() );
    }

    @Exported
    @Override
    public PxlObject __getitem__( PxlObject key )
    {
        if ( !(key instanceof PxlInteger) )
            throw new TypeError( "tuple indices must be integers" );

        int idx = (int) ((PxlInteger) key).getValue();
        if ( idx >= tuple.size() )
            throw new IndexError( "tuple index out of range" );

        return tuple.get( idx );
    }

    @Exported
    @Override
    public PxlIterator __getiterator__()
    {
        return new PxlBaseIterator( tuple.listIterator() );
    }

    @Exported
    @Override
    public PxlInteger __len__()
    {
        return new PxlInteger( tuple.size() );
    }

    // "Public" methods

    @Exported
    public PxlObject get( PxlObject idx )
    {
        if ( !(idx instanceof PxlInteger) )
            throw new TypeError( "tuple indices  must be integers" );

        int i = (int) ((PxlInteger) idx).getValue();
        return tuple.get( i );
    }

    // Attributes

    private static final Map<String, PxlObject> attrs = PxlObject
            .getExportedMethods( PxlTuple.class );

    @Override
    protected PxlObject getAttribute( String name )
    {
        return attrs.get( name );
    }

    @Override
    protected Collection<String> getAttributeNames()
    {
        return attrs.keySet();
    }

}
